The sample
provides a number of different examples that you can run. They
illustrate each stage of the process of applying exception handling described in this article. However,
this article describes an iterative process of updating a single
application scenario. To make it easier to see the results of each
stage, we have provided separate examples for each of them.
Note:
If you run the examples under the Visual Studio
debugger, you will find that the code halts when an exception
occurs—before it is sent to the Exception Handling block. You can
press F5 at this point to continue execution. Alternatively, you can
run the examples by pressing Ctrl-F5 (non-debugging mode) to prevent
this from happening.
To see how you can apply exception handling strategies and
configure exception handling policies, we'll start with a simple example
that causes an exception when it executes. First, we need a class that
contains a method that we can call from our main routine, such as the
following in the SalaryCalculator class
of the example application.
public Decimal GetWeeklySalary(string employeeId, int weeks)
{
String connString = string.Empty;
String employeeName = String.Empty;
Decimal salary = 0;
try
{
connString = ConfigurationManager.ConnectionStrings
["EmployeeDatabase"].ConnectionString;
// Access database to get salary for employee here...
// In this example, just assume it's some large number.
employeeName = "John Smith";
salary = 1000000;
return salary / weeks;
}
catch (Exception ex)
{
// provide error information for debugging
string template = "Error calculating salary for {0}."
+ " Salary: {1}. Weeks: {2}\n"
+ "Data connection: {3}\n{4}";
Exception informationException = new Exception(
string.Format(template, employeeName, salary, weeks,
connString, ex.Message));
throw informationException;
}
}
You can see that a call to the GetWeeklySalary method will cause an exception of
type DivideByZeroException when called
with a value of zero for the number of weeks parameter. The exception
message contains the values of the variables used in the calculation,
and other information useful to administrators when debugging the
application. Unfortunately, the current code has several issues. It
trashes the original exception and loses the stack trace, preventing
meaningful debugging. Even worse, the global exception handler for the
application presents any user of the application with all of the
sensitive information when an error occurs.
If you run the example for this article, and select option
Typical Default Behavior without Exception
Shielding, you will see this result generated by the code in
the catch statement:
Exception type System.Exception was thrown.
Message: 'Error calculating salary for John Smith.
Salary: 1000000. Weeks: 0
Connection: Database=Employees;Server=CorpHQ;
User ID=admin;Password=2g$tXD76qr Attempted to divide by zero.'
Source: 'ExceptionHandlingExample'
No inner exception
Applying Exception Shielding
It's clear that the application as it stands has a severe
security hole that allows it to reveal sensitive information. Of
course, we could prevent this by not adding the sensitive information
to the exception message. However, the information will be useful to
administrators and developers if they need to debug the application.
For example, if the data connection had failed or the database
contained invalid data, they would have seen this through missing
values for the employee name or salary; and they could see if the
configuration file contains the correct database connection string.
Alternatively, in the case shown here, they can immediately tell that
the database returned the required values for the operation, but the
user interface allowed the user to enter the value zero for the number
of weeks.
To provide this extra information, yet apply exception
shielding, you may consider implementing configuration settings and
custom code to allow administrators to specify when they need the
additional information. However, this is exactly where the Exception Handling block comes in. You can set up an
exception handling policy that administrators can modify as required,
without needing to write custom code or set up custom configuration
settings.
The first step is to create an exception handling policy that
specifies the events you want to handle, and contains a handler that
will either wrap (hide) or replace (remove) the exception containing
all of the debugging information with one that contains a simple error message suitable for display to users or
propagation through the layers of the application. You'll see these
options implemented in the following sections. You will also see how
you can log the original exception before replacing it, how you can
handle specific types of exceptions, and how you can apply exception
shielding to WCF services.